package com.evil.activiti.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.evil.activiti.service.FlowConditionService;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.common.activiti.enums.NodeTypeEnum;
import com.evil.common.activiti.enums.SequenceFlowTypeEnum;
import com.evil.common.application.dto.judge.JudgeAnd;
import com.evil.common.application.dto.judge.JudgeOr;
import com.evil.common.application.dto.process.node.NodeBaseDTO;
import com.evil.common.application.enums.judge.JudgeSignEnum;
import com.evil.common.account.dto.user.UserEnterpriseDTO;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.exception.BusinessException;
import org.activiti.bpmn.model.MultiInstanceLoopCharacteristics;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流工具类
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
public class ActivityUtil {

    /**
     * 节点坐标步进值
     */
    public final static Double FLOW_STEP = 200d;

    /**
     * 节点宽高值
     */
    public final static Double FLOW_SIZE = 100d;

    /**
     * 流程参数整合key
     */
    public final static String VARIABLES = "variables";

    /**
     * 流转类型
     */
    public final static String COMPLETE_TYPE = "completeType";

    /**
     * 流转备注
     */
    public final static String REMARK = "remark";

    /**
     * 处理人
     */
    public final static String ASSIGNEE = "assignee";

    /**
     * 处理人列表
     */
    public final static String ASSIGNEE_LIST = "assigneeList";

    /**
     * 已分配处理人列表
     */
    public final static String IS_COUNTERSIGN = "isCountersign";

    /**
     * 已分配处理人列表
     */
    public final static String ASSIGNED_LIST = "assignedList";

    /**
     * 已经完成的实例数量
     */
    public final static String nrOfCompletedInstances = "approval_countersign_approved";

    /**
     * 会签中总共的实例数
     */
    public final static String nrOfInstances = "nrOfInstances";

    /**
     * 当前活动的实例数量，即还没有完成的实例数量
     */
    public final static String nrOfActiveInstances = "nrOfActiveInstances";

    /**
     * 用于记录回退节点id
     */
    public final static String FALLBACK_NODE_ID = "fallback_node_id";

    /**
     * 连接符
     */
    private final static String CONNECTOR = "__";

    /**
     * 流程固定参数：订单id
     */
    private final static String ORDER_ID = "orderId";

    /**
     * 流程部署Process对象id前缀（因为流程不允许纯数字的命名）
     */
    private final static String PROCESS_PREFIX = "process";

    /**
     * 节点对象id前缀（因为流程不允许纯数字的命名）
     */
    private final static String FLOW_NODE_PREFIX = "flow_node";

    /**
     * 回退停止 节点对象id
     */
    private final static String STOP_NODE_PREFIX = "stop_node";

    /**
     * 拒绝停止 对象id
     */
    private final static String REFUSE_NODE_PREFIX = "refuse_node";

    /**
     * 控件条件前缀
     */
    private final static String CONTROL_JUDGE_KEY_PREFIX = "control_judge_key";

    /**
     * 流程节点流转条件key
     */
    private final static String PROCESS_NODE_COMPLETE_KEY = "process_node_complete_key";

    /**
     * 流程节点流转条件值
     */
    private final static String PROCESS_NODE_COMPLETE_CONDITION = "process_node_complete_condition";

    /**
     * 流程节点回退条件值
     */
    private final static String PROCESS_NODE_FALL_BACK_CONDITION_PREFIX = "process_node_fall_back_condition";

    /**
     * 流程节点拒绝条件值
     */
    private final static String PROCESS_NODE_REFUSE_CONDITION = "process_node_refuse_condition";

    /**
     * 用于记录当前流程 SequenceFlow 信息，从xx到yy -> SEQUENCE_FLOW_KEY_xx:yy
     */
    private final static String SEQUENCE_FLOW_KEY = "SEQUENCE_FLOW_KEY";

    /**
     * 流程固定参数：订单id
     *
     * @return String
     */
    public static String getProcessOrderIdKey() {
        return ORDER_ID;
    }

    /**
     * 转换工作流用户
     *
     * @param userEnterpriseDTO userEnterpriseDTO
     * @return String
     */
    public static String toActivitiLoginUser(UserEnterpriseDTO userEnterpriseDTO) {
        return ActivityUtil.connector(userEnterpriseDTO.getEnterpriseId(), userEnterpriseDTO.getUserId());
    }

    /**
     * 转换工作流用户
     *
     * @param userEnterpriseDTOList userEnterpriseDTOList
     * @return String
     */
    public static List<String> toActivitiLoginUsers(List<UserEnterpriseDTO> userEnterpriseDTOList) {
        return userEnterpriseDTOList.stream().map(ActivityUtil::toActivitiLoginUser).collect(Collectors.toList());
    }

    /**
     * 还原用户id
     *
     * @param username username
     * @return userId
     */
    public static UserEnterpriseDTO restoreUserId(String username) {
        String[] split = username.split(CONNECTOR);
        return new UserEnterpriseDTO(Long.valueOf(split[0]), Long.valueOf(split[1]));
    }

    /**
     * 还原用户ids
     *
     * @param usernames usernames
     * @return userId
     */
    public static List<UserEnterpriseDTO> restoreUserIds(Collection<String> usernames) {
        return usernames.stream().map(ActivityUtil::restoreUserId).collect(Collectors.toList());
    }

    /**
     * 获取工作流登录用户
     *
     * @return UserEnterpriseDTO
     */
    public static String getActivitiLoginUser() {
        return SecurityContextHolder.getContext().getAuthentication().getName();
    }

    /**
     * 获取用户企业信息
     *
     * @return UserEnterpriseDTO
     */
    public static UserEnterpriseDTO getUserEnterpriseByActivitiLoginUser() {
        return ActivityUtil.restoreUserId(ActivityUtil.getActivitiLoginUser());
    }

    /**
     * 获取
     *
     * @param processId processId
     * @return String
     */
    public static String getResourceName(Long processId) {
        return String.format("%s.bpmn", processId);
    }

    /**
     * 获取ProcessId
     *
     * @param processId processId
     * @return String
     */
    public static String getProcessId(Long processId) {
        return ActivityUtil.connector(PROCESS_PREFIX, processId);
    }

    /**
     * 生成节点id
     *
     * @param fallbackNode fallbackNode
     * @return String
     */
    public static String generateFlowNodeId(NodeBaseDTO fallbackNode) {
        return ActivityUtil.generateFlowNodeId(fallbackNode.getNodeType(), fallbackNode.getNodeId());
    }

    /**
     * 生成节点id
     *
     * @param nodeType nodeId
     * @return String
     */
    public static String generateFlowNodeId(Integer nodeType, Long nodeId) {
        return ActivityUtil.connector(FLOW_NODE_PREFIX, nodeType, nodeId);
    }

    /**
     * 还原节点id
     *
     * @param flowId flowId
     * @return String
     */
    public static Long restoreFlowNodeId(String flowId) {
        if (StringUtils.isBlank(flowId)) {
            throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "节点id不能为空");
        }
        return Long.valueOf(flowId.split(CONNECTOR)[2]);
    }

    /**
     * 还原节点type
     *
     * @param flowId flowId
     * @return String
     */
    public static Integer restoreFlowNodeType(String flowId) {
        if (StringUtils.isBlank(flowId)) {
            throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "节点id不能为空");
        }
        return Integer.valueOf(flowId.split(CONNECTOR)[1]);
    }

    /**
     * 还原节点type
     *
     * @param flowId flowId
     * @return String
     */
    public static NodeTypeEnum restoreFlowNodeTypeEnum(String flowId) {
        return NodeTypeEnum.isExistById(ActivityUtil.restoreFlowNodeType(flowId));
    }

    /**
     * 回退停止 节点对象id
     *
     * @return flowId
     */
    public static String generateStopNodeId() {
        return ActivityUtil.connector(STOP_NODE_PREFIX, NodeTypeEnum.END_EVENT.getId(), 0);
    }

    /**
     * 拒绝停止 对象id
     *
     * @return flowId
     */
    public static String generateRefuseNodeId() {
        return ActivityUtil.connector(REFUSE_NODE_PREFIX, NodeTypeEnum.END_EVENT.getId(), 0);
    }

    /**
     * 还原连线id 连线类型
     *
     * @param sequenceFlowId sequenceFlowId
     * @return SequenceFlowTypeEnum
     */
    public static String restoreFlowType(String sequenceFlowId) {
        if (StringUtils.isBlank(sequenceFlowId)) {
            throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "连线id不能为空");
        }
        return sequenceFlowId.split(CONNECTOR)[0];
    }

    /**
     * 还原连线id 连线类型
     *
     * @param sequenceFlowId sequenceFlowId
     * @return SequenceFlowTypeEnum
     */
    public static SequenceFlowTypeEnum restoreFlowTypeEnum(String sequenceFlowId) {
        return SequenceFlowTypeEnum.isExistByType(ActivityUtil.restoreFlowType(sequenceFlowId));
    }

    /**
     * 获取节点到达事件变量key
     *
     * @param flowId flowId
     * @return String
     */
    public static String generateFlowNodeStartTimeKey(String flowId) {
        return ActivityUtil.connector("flow_node_start_time", flowId);
    }

    /**
     * 流程节点流转条件key
     *
     * @return String
     */
    public static String getProcessCompleteKey(Long controlId) {
        if (null != controlId && !Long.valueOf(0).equals(controlId)) {
            return ActivityUtil.connector(CONTROL_JUDGE_KEY_PREFIX, controlId);
        } else {
            return PROCESS_NODE_COMPLETE_KEY;
        }
    }

    /**
     * 流程节点流转条件是否是控件值
     *
     * @param key key
     * @return boolean
     */
    public static boolean isControlValue(String key) {
        if (StringUtils.isNotBlank(key)) {
            return key.startsWith(CONTROL_JUDGE_KEY_PREFIX);
        }
        return false;
    }

    /**
     * 流程节点流转条件是否是控件值
     *
     * @param key key
     * @return boolean
     */
    public static Long restoreControlId(String key) {
        if (StringUtils.isNotBlank(key) && ActivityUtil.isControlValue(key)) {
            return Long.valueOf(key.split(CONNECTOR)[1]);
        }
        return null;
    }

    /**
     * 获取流程节点流转条件值
     *
     * @return String
     */
    public static List<String> getProcessNodeCompleteCondition() {
        return Collections.singletonList(PROCESS_NODE_COMPLETE_CONDITION);
    }

    /**
     * 生成流转条件
     *
     * @return List
     */
    public static List<JudgeOr> getProcessNodeCondition(List<String> values) {
        // 流转条件
        JudgeAnd and = ActivityUtil.createInputEqualsJudgeAnd();
        and.setValues(values);

        List<JudgeOr> ors = new ArrayList<>();
        JudgeOr or = new JudgeOr();
        or.setJudgeAnd(Collections.singletonList(and));
        ors.add(or);
        return ors;
    }

    /**
     * 获取流程节点驳回条件值
     *
     * @return String
     */
    public static List<String> getProcessNodeFallBackCondition(String flowId) {
        String condition = ActivityUtil.connector(PROCESS_NODE_FALL_BACK_CONDITION_PREFIX, flowId);
        return Collections.singletonList(condition);
    }

    /**
     * 获取流程节点拒绝条件值
     *
     * @return String
     */
    public static List<String> getProcessNodeRefuseCondition() {
        return Collections.singletonList(PROCESS_NODE_REFUSE_CONDITION);
    }

    /**
     * 用于记录当前流程 SequenceFlow 信息，从xx到yy -> SEQUENCE_FLOW_KEY_yy:xx
     *
     * @return String
     */
    public static String getSequenceFlowKey(Long nodeId) {
        return ActivityUtil.connector(SEQUENCE_FLOW_KEY, nodeId);
    }

    /**
     * 获取校验条件组
     *
     * @param judgeOrs judgeOrs
     * @param negation 条件取反
     * @return String
     */
    public static String conditionExpression(List<JudgeOr> judgeOrs, boolean negation) {
        List<String> conditionOrs = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(judgeOrs)) {
            for (JudgeOr or : judgeOrs) {
                List<String> conditionAnds = new ArrayList<>();
                // 且条件
                for (JudgeAnd and : or.getJudgeAnd()) {
                    String conditionAnd = ActivityUtil.getJudge(and);
                    if (StringUtils.isNotBlank(conditionAnd)) {
                        conditionAnds.add(conditionAnd);
                    }
                }
                conditionOrs.add(String.format("%s", String.join(JudgeAnd.sign, conditionAnds)));
            }
        }
        if (!conditionOrs.isEmpty()) {
            String format = negation ? "${!(%s)}" : "${%s}";
            return String.format(format, String.join(JudgeOr.sign, conditionOrs));
        } else {
            return "";
        }
    }

    /**
     * 获取校验条件
     *
     * @param judgeAnd judgeAnd
     * @return String
     */
    public static String getJudge(JudgeAnd judgeAnd) {
        ControlCodeEnum.findByCode(judgeAnd.getControlCode());
        JudgeSignEnum.isExistById(judgeAnd.getJudgeSign());
        return String.format(FlowConditionService.FLOW_CONDITION_FUNCTION, ActivityUtil.VARIABLES, JSON.toJSONString(judgeAnd));
    }

    /**
     * 生成文本框 等判断符 且条件
     *
     * @return JudgeAnd
     */
    public static JudgeAnd createInputEqualsJudgeAnd() {
        JudgeAnd and = new JudgeAnd();
        and.setJudgeSign(JudgeSignEnum.EQUALS.getId());
        and.setControlCode(ControlCodeEnum.INPUT.getCode());
        return and;
    }

    /**
     * 连接符连接
     *
     * @param objs objs
     * @return String
     */
    public static String connector(Object... objs) {
        StringJoiner stringJoiner = new StringJoiner(CONNECTOR);
        for (Object o : objs) {
            if (null != o) {
                stringJoiner.add(o.toString());
            }
        }
        return stringJoiner.toString();
    }

    public static double flowStep(Double d, int multiple) {
        return d + (ActivityUtil.FLOW_STEP * multiple);
    }

    /**
     * 检查是否是会签
     *
     * @param loopCharacteristics loopCharacteristics
     * @return boolean
     */
    public static boolean checkLoopCharacteristics(MultiInstanceLoopCharacteristics loopCharacteristics) {
        return null != loopCharacteristics && !"1".equals(loopCharacteristics.getLoopCardinality());
    }

    /**
     * 从variable 获取列表
     *
     * @param variables variable
     * @param name      name
     * @param tClass    tClass
     * @return List
     */
    public static <T> List<T> getListForVariables(Map<String, Object> variables, String name, Class<T> tClass) {
        List<?> list = MapUtil.get(variables, name, List.class, new ArrayList<T>());
        return ActivityUtil.listConvert(list, tClass);
    }

    /**
     * 列表 类型转换
     *
     * @param list   list
     * @param tClass tClass
     * @param <T>    <T>
     * @return List
     */
    public static <T> List<T> listConvert(List<?> list, Class<T> tClass) {
        return list.stream().map(e -> Convert.convert(tClass, e, null)).collect(Collectors.toList());
    }
}
